/*
 *  Databaser.cpp
 *  openFrameworks
 *
 *  Created by fregment on 24.05.09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "Databaser.h"

namespace abliefern {
	
	
	
	
	////--- save
	
	
	int Databaser::saveImage(KlausImage* theImage){
		
		std::string theFilename = theImage->filename;
		
		//--- check if an image with the filename already exists in the database
		
		
		int _id = Databaser::returnIDFromPicture(theFilename.c_str());
		
		if(_id != -1){													// if is already exists just return the ID
			
			
			printf("the image already exists in the database\n");
			
			theImage->databaseID=_id;
			return -1;
			
		}
		else{												// else store the imagedata in the database and return the new ID
			
			int _myReturnValue;
			char instertString [500];
			_myReturnValue = sprintf(instertString, "INSERT INTO picture(filename, width, height)VALUES('%s',%i,%i);", theFilename.c_str(),theImage->width,theImage->height);
			_myReturnValue = requestDB(instertString);
			
			int _theNewID = Databaser::returnIDFromPicture(theFilename.c_str());
			
			
			
			theImage->databaseID=_theNewID;
			return _theNewID;
			
			
			
			
			
		}
		
		
	}
	
	
	std::vector<VanishingPoint>* Databaser::getVanishingPointsOfImage (KlausImage* theImage){
		
		std::vector<VanishingPoint>* _myReturnVPs = new std::vector<VanishingPoint>();
		int _myReturn;
		char searchString [800];
		_myReturn = sprintf(searchString, "select v.PrimaryKey, v.xPosition, v.yPosition from vanishingPoint as v inner join picture as p on v.fotoID = p.PrimaryKey where p.filename  = '%s';", theImage->filename.c_str());
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i+=3){
				
				//std::string s=   _myVdata.at(i);
				//printf("%i:%s\n",i,s.c_str());
				
				
				int _databaseID = Databaser::toInteger(&_myVdata.at(i));
				float _xPos = Databaser::toFloat(&_myVdata.at(i+1));
				float _yPos = Databaser::toFloat(&_myVdata.at(i+2));
				
				
				
				
				VanishingPoint* _myVanishingPoint = new VanishingPoint();
				_myVanishingPoint->x=_xPos;
				_myVanishingPoint->y=_yPos;
				_myVanishingPoint->databaseID=_databaseID;
				
				_myReturnVPs->push_back(*_myVanishingPoint);
				
				
			}
			return _myReturnVPs;
		}
		

		return _myReturnVPs;
		
	}
	
	
	void Databaser::saveImageSegment(ImageSegment* theSegment,VanishingPoint* theVanishingPoint){
		
		
		
		int _imageID= returnImageIDfromVanishingPoint(theVanishingPoint->databaseID);
		
		int _myReturnValue;
		char instertString [800];
		_myReturnValue = sprintf(instertString, "INSERT INTO segment(filename,width,height, xPosition, yPosition,fotoID,vpID, borderPercentage) VALUES('%s',%i,%i,%f,%f,%i,%i,%f);", 
								 theSegment->filename.c_str(),theSegment->width,theSegment->height, theSegment->x,theSegment->y,_imageID,theVanishingPoint->databaseID, theSegment->borderPercentage);
		_myReturnValue = requestDB(instertString);

	}
	
	
	void Databaser::saveVanishingPoint(KlausImage* theImage, VanishingPoint* theVanishingPoint){
		
		int _imageID;
		
		if(theImage->databaseID != -1){    // imageObject already has an ID
			
			_imageID = theImage->databaseID;
			
		}else {					// else fetch the ID from database
			
			std::string theFilename = theImage->filename;
			_imageID = Databaser::returnIDFromPicture(theFilename.c_str());
			
		}
		
		int _myReturnValue;
		
		
		//-- save the vanishing point with the imageId
		
		char instertString [500];
		_myReturnValue = sprintf(instertString, "INSERT INTO vanishingPoint(fotoID, xPosition, yPosition) VALUES(%i,%f,%f);", _imageID, theVanishingPoint->x , theVanishingPoint->y);
		_myReturnValue = requestDB(instertString);
		
		
		int _theID = Databaser::returnIDFromVanishingPoint( _imageID, theVanishingPoint->x , theVanishingPoint->y);
		theVanishingPoint->databaseID=_theID;
		
	}
	
	
	void Databaser::setImageState(KlausImage* theImage, int theState){
		
		printf("Image id is %i with state %i\n", theImage->databaseID, theState);
		
		int _imageID;
		
		if(theImage->databaseID != -1){    // imageObject already has an ID
			
			_imageID = theImage->databaseID;
			
			
		}else {					// else fetch the ID from database
			
			std::string theFilename = theImage->filename;
			_imageID = Databaser::returnIDFromPicture(theFilename.c_str());
			
		}
		
		int _myReturnValue;
		
		char updateString [500];
		_myReturnValue = sprintf(updateString, "UPDATE picture SET state=%i where PrimaryKey =%i;",theState,_imageID);
		_myReturnValue = requestDB(updateString);
		
	}
	
	
	

	void Databaser::saveKeywords(std::vector<std::string> theKeywords, int theProjectID ){

		for(int i=0; i< theKeywords.size(); i++){  // for each keyword

		std::string myTag =	theKeywords.at(i);
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "SELECT * FROM tag where tagName='%s' AND projectID=%i;",myTag.c_str(),theProjectID); 
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() == 0){  // the tag is not in the projectName write it to the DB
		char instertString [500];
		_myReturn = sprintf(instertString, "INSERT INTO  tag(tagName, projectID) VALUES('%s',%i);",myTag.c_str(),theProjectID);
		_myReturn = requestDB(instertString);
		
		}
		}
		
	}
	
	
	
	
	
	
	
	
	/// --get
	
	
	KlausImage* Databaser::getImageFromId(int theId){
		
		
		
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select filename from picture where PrimaryKey = %i;",theId); 
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			return new KlausImage(_myVdata.at(0));
		}
		
		return NULL;
		
		
		
	}
	
	
	KlausImage* Databaser::getImageWithState(ImageStateEnum theState){
		
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select PrimaryKey, filename, width, height  from picture where state = %i LIMIT 1;", theState); 
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			return new KlausImage(_myVdata.at(1), toInteger(&_myVdata.at(0)), toInteger(&_myVdata.at(1)), toInteger(&_myVdata.at(2)));
		}
		
		return NULL;
		
	}
	
	
	
	std::vector<ImageSegment>* Databaser::retrieveAllSegments(float theBorderPercentageMin,float theBorderPercentageMax, std::vector<std::string>* theKeywords){
		
		ostringstream myQuery;
		
		
				
		
		
		myQuery 
		<< "SELECT s.PrimaryKey, s.filename, s.xPosition, s.yPosition, s.width, s.height, s.width * s.height AS area, s.xPosition  AS middleX, s.yPosition AS middleY FROM segment AS s INNER JOIN picture AS p ON p.primaryKey = s.fotoID " 
		<< " WHERE s.borderPercentage <= " <<  theBorderPercentageMax <<  " AND s.borderPercentage >= " << theBorderPercentageMin;		
		
		if(theKeywords==NULL || theKeywords->size()==0){  // no keywords as attributes
			myQuery << "AND 1=2;"; // do not allow the GAU
			
			printf("YOU CANNOT DO THAT WITHOUTKEYWORDS!!!!!!!!!!!!!!!!!!!\n");
			
		}
		
		
		
		else{			
			
			for(int i=0; i<theKeywords->size();i++){
				printf("WITH___KEYWORDS!!!!!!!!!!!!!!!!!!!\n");
				printf("KeywordsInSearch:%s\n",theKeywords->at(i).c_str());
				
			}
			
			
			
			// add project ID to search   
			myQuery << " AND p.projectID IN(";
			
			std::vector<int> _myProjectIDs = Databaser::retrieveProjectIDsWithKeywords(theKeywords);
			printf("MY Project__IDs Count:%i______\n",_myProjectIDs.size());
			
			for(int i = 0; i < _myProjectIDs.size(); i++){
				myQuery << _myProjectIDs.at(i);
				printf("WITH___PROJECT__ID:%i______\n",_myProjectIDs.at(i));
				
				if(i < _myProjectIDs.size() - 1){
					myQuery << ",";
				}
				
			}
			
			myQuery << ");";
			
		}
		
		
		std::cout << "El query es: " << myQuery.str().c_str() << "\n";
		
		
		std::vector<ImageSegment>* _myReturnSegments = new std::vector<ImageSegment>();
		
		int _myReturn;
		
		_myReturn = requestDB(myQuery.str().c_str());
		
		if(_myReturn == SQLITE_OK){
			
			std::cout << "SQLITE_OK " << _myVdata.size()<<"\n";
			
			
		}
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i+=9){
				
				//std::string s=   _myVdata.at(i);
				//printf("%i:%s\n",i,s.c_str());
				
				int _databaseID = Databaser::toInteger(&_myVdata.at(i));
				
				std::string _filename = _myVdata.at(i + 1);
				
				float _xPos = Databaser::toFloat(&_myVdata.at(i+2));
				float _yPos = Databaser::toFloat(&_myVdata.at(i+3));
				
				int _width = Databaser::toInteger(&_myVdata.at(i+4));
				int _height = Databaser::toInteger(&_myVdata.at(i+5));
				
				
				ImageSegment _myImageSegment(_filename,_xPos,_yPos,_width,_height);
				_myImageSegment.databaseID = _databaseID;
				
				
				_myReturnSegments->push_back(_myImageSegment);
				
				
			}
			
			
			
			return _myReturnSegments;
			
			
			
		}
		
		
		return NULL;
		
	}
	
	
	std::vector<ImageSegment>* Databaser::retrieveGoodSegements(float theX, float theY, float theTolerance, float theMinArea, float theMaxArea,float theBorderPercentageMin,float theBorderPercentageMax, std::vector<int>* theIgnores, std::vector<std::string>* theKeywords){
		
		ostringstream myQuery;
		
		
		int myMinX = theX - theTolerance;
		int myMinY = theY - theTolerance;
		
		int myMaxX = theX + theTolerance;
		int myMaxY = theY + theTolerance;
		
		
		int myMinArea = theMinArea;
		int myMaxArea = theMaxArea;
		
		

		
		myQuery << "SELECT s.PrimaryKey, s.filename, s.xPosition, s.yPosition, s.width, s.height, s.width * s.height AS area, s.xPosition  AS middleX, s.yPosition AS middleY FROM segment AS s INNER JOIN picture AS p ON p.primaryKey = s.fotoID WHERE area > " 
				<< myMinArea << " AND area < " << myMaxArea << " AND middleX > " << myMinX << " AND middleX < " << myMaxX << " AND middleY > " << myMinY << " AND middleY < " << myMaxY 
				<< " AND s.borderPercentage <= " <<  theBorderPercentageMax <<  " AND s.borderPercentage >= " << theBorderPercentageMin << " AND s.primaryKey NOT IN (";
		
		for(int i = 0; i < theIgnores->size(); i++){
			myQuery << theIgnores->at(i);
			
			if(i < theIgnores->size() - 1){
				myQuery << ",";
			}
			
		}
	
		if(theKeywords==NULL || theKeywords->size()==0){  // no keywords as attributes
			myQuery << ");";
			
			printf("WITHOUTKEYWORDS!!!!!!!!!!!!!!!!!!!\n");
		
		}
		
		
		
		else{			
			
			for(int i=0; i<theKeywords->size();i++){
				printf("WITH___KEYWORDS!!!!!!!!!!!!!!!!!!!\n");
				printf("KeywordsInSearch:%s\n",theKeywords->at(i).c_str());
				
			}
			
			
			
			// add project ID to search   
			myQuery << ") AND p.projectID IN(";
			
			std::vector<int> _myProjectIDs = Databaser::retrieveProjectIDsWithKeywords(theKeywords);
			printf("MY Project__IDs Count:%i______\n",_myProjectIDs.size());
			
			for(int i = 0; i < _myProjectIDs.size(); i++){
				myQuery << _myProjectIDs.at(i);
				printf("WITH___PROJECT__ID:%i______\n",_myProjectIDs.at(i));
				
				if(i < _myProjectIDs.size() - 1){
					myQuery << ",";
				}
				
			}

		myQuery << ");";
		
		}
		
		
		std::cout << "El query es: " << myQuery.str().c_str() << "\n";
		
		
		std::vector<ImageSegment>* _myReturnSegments = new std::vector<ImageSegment>();
		
		int _myReturn;
		
		_myReturn = requestDB(myQuery.str().c_str());
		
		if(_myReturn == SQLITE_OK){
			
			std::cout << "SQLITE_OK " << _myVdata.size()<<"\n";
			
			
		}
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i+=9){
				
				//std::string s=   _myVdata.at(i);
				//printf("%i:%s\n",i,s.c_str());
				
				int _databaseID = Databaser::toInteger(&_myVdata.at(i));
				
				std::string _filename = _myVdata.at(i + 1);
				
				float _xPos = Databaser::toFloat(&_myVdata.at(i+2));
				float _yPos = Databaser::toFloat(&_myVdata.at(i+3));
				
				int _width = Databaser::toInteger(&_myVdata.at(i+4));
				int _height = Databaser::toInteger(&_myVdata.at(i+5));
				
				
				ImageSegment _myImageSegment(_filename,_xPos,_yPos,_width,_height);
				_myImageSegment.databaseID = _databaseID;
				
				
				_myReturnSegments->push_back(_myImageSegment);
				
				
			}
			
			
						
			return _myReturnSegments;
			
			
			
		}
		
		
		return NULL;
		
		
		
	}
	
	
	
	
	std::vector<int> Databaser::retrieveProjectIDsWithKeywords(std::vector<std::string>* theKeywords){
		
		
		
		ostringstream myProjectQuery;

		myProjectQuery << "SELECT DISTINCT projectID FROM tag WHERE tagName IN(";
		
		for(int i=0; i< theKeywords->size(); i++){
			myProjectQuery << "'" << theKeywords->at(i) << "'";
			
			if(i < theKeywords->size() - 1){
				myProjectQuery << ",";
			}
		}
		
		myProjectQuery << ");";
		
		int _myReturn;
		
		
		
		_myReturn = Databaser::requestDB(myProjectQuery.str().c_str());
		
		
		std::vector<int> _returnProjectIDs;
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i++){
				
				
				
				int _projectID = Databaser::toInteger(&_myVdata.at(i));
				
				
				_returnProjectIDs.push_back(_projectID);
			}
		
			return _returnProjectIDs;
		}
		
		
		return _returnProjectIDs;
		
	
	}
	
	
	
	
	
	
	
	
	
	
	
	std::vector<ImageSegment>* Databaser::getSegmentsOfImage (KlausImage* theImage){
		
		
		std::vector<ImageSegment>* _myReturnSegments = new std::vector<ImageSegment>;
		int _myReturn;
		char searchString [800];
		_myReturn = sprintf(searchString, "select s.filename, s.xPosition, s.yPosition, s.width, s.height from segment as s inner join picture as p on s.fotoID = p.PrimaryKey where p.filename  = '%s';", theImage->filename.c_str());
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i+=5){
				
				//std::string s=   _myVdata.at(i);
				//printf("%i:%s\n",i,s.c_str());
				
				std::string _filename = _myVdata.at(i);
				
				float _xPos = Databaser::toFloat(&_myVdata.at(i+1));
				float _yPos = Databaser::toFloat(&_myVdata.at(i+2));
				
				int _width = Databaser::toInteger(&_myVdata.at(i+3));
				int _height = Databaser::toInteger(&_myVdata.at(i+4));
				
				ImageSegment _myImageSegment(_filename,_xPos,_yPos,_width,_height);
				_myReturnSegments->push_back(_myImageSegment);
				
				
			}
			return _myReturnSegments;
		}
		
		
		
		
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	std::vector<ImageSegment>* Databaser::getSegmentsOfVanishingPoint(VanishingPoint* thePoint){
		
		int _pointID;
		
		if(thePoint->databaseID != -1){
			_pointID=thePoint->databaseID;
		}
		else{
			printf("the vanishingPoint object needs a databaseID!");
			return NULL;
			
		}
		
		std::vector<ImageSegment>* _myReturnSegments = new std::vector<ImageSegment>;
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select filename, xPosition, yPosition, width, height from segment where vpID  = %i;", thePoint->databaseID);
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i+=5){
				
				//std::string s=   _myVdata.at(i);
				//printf("%i:%s\n",i,s.c_str());
				
				std::string _filename = _myVdata.at(i);
				
				float _xPos = Databaser::toFloat(&_myVdata.at(i+1));
				float _yPos = Databaser::toFloat(&_myVdata.at(i+2));
				
				int _width = Databaser::toInteger(&_myVdata.at(i+3));
				int _height = Databaser::toInteger(&_myVdata.at(i+4));
				
				ImageSegment _myImageSegment(_filename,_xPos,_yPos,_width,_height);
				_myReturnSegments->push_back(_myImageSegment);
				
				
			}
			return _myReturnSegments;
		}
		
		return NULL;
		
		
	}
	
	
	std::vector<KlausImage> Databaser::retrieveImagesWithState(ImageStateEnum theState){
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select filename, xPosition, yPosition, width, height from picture where state  = %i;", theState);
		_myReturn = requestDB(searchString);
		
		
		std::vector<KlausImage> bla;
		
		printf("Image state %d", theState);
		
		return bla;
		
	}

	
	
	
	std::vector<std::string> Databaser::retrieveKeywordsForInterface(){
	
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "SELECT DISTINCT tagName FROM tag ORDER BY tagName;");
		_myReturn = requestDB(searchString);
		
		
		std::vector<std::string> _keywords;
		
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			for(int i=0; i < _myVdata.size(); i++){
					_keywords.push_back(_myVdata.at(i).c_str());
			}
		
		}
		
		
		
		return _keywords;
	
	
	
	
	
	
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	int Databaser::returnIDFromVanishingPoint(int theImageID,float theX,float theY){
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select PrimaryKey from vanishingPoint where fotoID = %i and xPosition = %f and yPosition = %f;",theImageID,theX,theY);
		_myReturn = requestDB(searchString);
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			return Databaser::toInteger(&_myVdata.at(0));
		}
		return -1;
	}
	
	
	int Databaser::returnImageIDfromVanishingPoint(int theVanishingPointID){
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select fotoID from vanishingPoint where PrimaryKey = %i;",theVanishingPointID);
		_myReturn = requestDB(searchString);
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			return Databaser::toInteger(&_myVdata.at(0));
		}
		return -1;
	}
	
	
	int Databaser::returnIDFromPicture(std::string theFilename){
		
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select PrimaryKey from picture where filename  = '%s';", theFilename.c_str());
		_myReturn = requestDB(searchString);
		if (_myReturn == SQLITE_OK && _myVdata.size() > 0){
			return Databaser::toInteger(&_myVdata.at(0));
		}
		return -1;
	}
	
	
	KlausImage* Databaser::searchPictureBySize(int theWidth, int theHeight, int theAberration){
		int _myReturn;
		char searchString [500];
		_myReturn = sprintf(searchString, "select filename from picture where %i <= width and width <= %i and %i <= height and height <= %i;", 
							theWidth-theAberration,theWidth+theAberration,theHeight-theAberration,theHeight+theAberration);
		_myReturn = requestDB(searchString);
		
		if (_myReturn == SQLITE_OK){
			for(int i=0; i < _myVdata.size() ; ++i){
				
				return new KlausImage(_myVdata.at(i));
				
				
			}
			
		}
		
		
		
		
		
	}
	
	
	
	
	
	// --delete
	
	
	
	void Databaser::deleteAllVanishingPoints(KlausImage* theImage){
		
		int _imageID;
		int _myReturnValue;
		
		if(theImage->databaseID != -1){
			_imageID=theImage->databaseID;
		}
		else{
			
			_imageID=returnIDFromPicture(theImage->filename);
		}
		
		
		char deleteString [500];
		_myReturnValue = sprintf(deleteString, "DELETE FROM vanishingPoint WHERE fotoID = %i;", _imageID);
		_myReturnValue = requestDB(deleteString);
	}
	
	void Databaser::deleteAllImageSegments(KlausImage* theImage){
		
		int _imageID;
		int _myReturnValue;
		
		if(theImage->databaseID != -1){
			_imageID=theImage->databaseID;
		}
		else{
			
			_imageID=returnIDFromPicture(theImage->filename);
		}
		
		
		char deleteString [500];
		_myReturnValue = sprintf(deleteString, "DELETE FROM segment WHERE fotoID = %i;", _imageID);
		_myReturnValue = requestDB(deleteString);
	}
	
	
	
	
	
	
	
	// --db handle
	
	
	
	
	
	void Databaser::openDB() {
		myRC = sqlite3_open(ofToDataPath("database/database.rsd", true).c_str(), &_myDB);
		
		printf("### Opening database from file %s\n ", ofToDataPath("database/database.rsd", true).c_str());
		
		if( myRC ){
			fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(_myDB));
			sqlite3_close(_myDB);
			
		}
		
	}	
	
	
	void Databaser::closeDB(){
		sqlite3_close(_myDB);
	} 
	
	
	
	int Databaser::requestDB(std::string theRequest) {
		myRC = sqlite3_get_table(
								 _myDB,              /* An open database */
								 theRequest.c_str(),       /* SQL to be executed */
								 &result,       /* Result written to a char *[]  that this points to */
								 &myNrow,             /* Number of result rows written here */
								 &myNcol,          /* Number of result columns written here */
								 &zErrMsg          /* Error msg written here */
								 );
		
		
		printf("rows: %d \n" ,myNrow);		
		if(_myVcol_head.size() > 0) {_myVcol_head.clear();}
		if(_myVdata.size()>0) {_myVdata.clear();}
		
		if( myRC == SQLITE_OK ){
			
			for(int i=0; i < myNcol; ++i)
				_myVcol_head.push_back(result[i]); /* First row heading */
			for(int i=0; i < myNcol*myNrow; ++i){
				_myVdata.push_back(result[myNcol+i]);
				
				//std::string s=   _myVdata.at(i);
				//printf("%s",s.c_str());
				//printf("\n");
				
				
			}
		}
		sqlite3_free_table(result);
		return myRC;
		
		
	}
	
	
	//--- helper
	
	
	int Databaser::toFloat(std::string* theString){
		
		int i = atof(theString->c_str());
		return i;
		
	}
	
	int Databaser::toInteger(std::string* theString){
		
		int i = atoi(theString->c_str());
		return i;
		
	}
	
	
	
	
	
	
	
}